package com.java.simple.study.juc.p7;

import com.java.simple.study.tool.Sleeper;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.locks.StampedLock;

/**
 * @author zhoujunlin
 * @date 2022年10月09日 21:40
 * @desc
 */
@Slf4j(topic = "c.TestStampedLock")
public class TestStampedLock {

    public static void main(String[] args) {
        /**
         * 21:55:14.456 c.DataContainerStamped [t2] - optimistic read locking...256
         * 21:55:14.456 c.DataContainerStamped [t1] - optimistic read locking...256
         * 21:55:14.459 c.DataContainerStamped [t2] - read finish...256, data:1
         * 21:55:15.464 c.DataContainerStamped [t1] - read finish...256, data:1
         */
        //testReadLock();

        /**
         * 21:58:56.316 c.DataContainerStamped [t2] - write lock 384
         * 21:58:56.316 c.DataContainerStamped [t1] - optimistic read locking...256
         * 21:58:57.328 c.DataContainerStamped [t1] - upgrade to read lock...256
         * 21:58:58.327 c.DataContainerStamped [t2] - write unlock 384
         * 21:58:58.327 c.DataContainerStamped [t1] - read lock 513
         * 21:58:59.339 c.DataContainerStamped [t1] - read finish...513, data:100
         * 21:58:59.339 c.DataContainerStamped [t1] - read unlock 513
         */
        testWriteLock();
    }

    private static void testWriteLock() {
        // 测试写锁导致时间戳发生改变 导致乐观锁升级读锁
        DataContainerStamped dataContainer = new DataContainerStamped(1);
        new Thread(() -> {
            dataContainer.read(1);
        }, "t1").start();

        Sleeper.sleep(0.5);
        new Thread(() -> {
            dataContainer.write(100);
        }, "t2").start();
    }

    private static void testReadLock() {
        // 测试读-读
        // 优化过后都是乐观锁 即多线程读并不会升级为读锁
        DataContainerStamped dataContainer = new DataContainerStamped(1);
        new Thread(() -> {
            dataContainer.read(1);
        }, "t1").start();

        Sleeper.sleep(0.5);

        new Thread(() -> {
            dataContainer.read(0);
        }, "t2").start();
    }
}

@Slf4j(topic = "c.DataContainerStamped")
class DataContainerStamped {

    private int data;

    private final StampedLock lock = new StampedLock();

    public DataContainerStamped(int data) {
        this.data = data;
    }

    public int read(int readTime) {
        // 乐观读锁
        long stamp = lock.tryOptimisticRead();
        log.debug("optimistic read locking...{}", stamp);
        // 模拟读取耗时
        Sleeper.sleep(readTime);
        if (lock.validate(stamp)) {
            log.debug("read finish...{}, data:{}", stamp, data);
            return data;
        }
        // 时间戳不对  乐观锁升级
        log.debug("upgrade to read lock...{}", stamp);
        try {
            stamp = lock.readLock();
            log.debug("read lock {}", stamp);
            Sleeper.sleep(readTime);
            log.debug("read finish...{}, data:{}", stamp, data);
            return data;
        } finally {
            log.debug("read unlock {}", stamp);
            lock.unlock(stamp);
        }
    }

    public void write(int newData) {
        long stamp = lock.writeLock();
        log.debug("write lock {}", stamp);
        try {
            Sleeper.sleep(2);
            this.data = newData;
        } finally {
            log.debug("write unlock {}", stamp);
            lock.unlockWrite(stamp);
        }
    }

}

